package org.qloudgen.borg.utils;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.qloudgen.borg.beans.TaskLogBean;
import org.qloudgen.borg.messaging.message.TaskMessage;
import org.qloudgen.borg.metadb.DefaultImpl;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.Session;

/**
* RemoteShellTool
*/

public class RemoteShellExec {
	
	static Log log = LogFactory.getLog(RemoteShellExec.class);

	private Connection conn;
	private String ip;
	private String charset = Charset.defaultCharset().toString();
	private String userName;
	private String password;
	private Session session;
	private boolean isRunnable = true;
	
	public RemoteShellExec(String ip, String userName, String password, String charset) {
	   this.setIp(ip);
	   this.userName = userName;
	   this.password = password;
	   if(charset != null) {
	    this.charset = charset;
	   }
	}
	
	public RemoteShellExec(String ip, String userName, String password) {
	   this(ip,userName,password,null);
	}
	
	public boolean login() throws IOException {
		conn = new Connection(ip);
		conn.connect(); 
		return conn.authenticateWithPassword(userName, password);
	}
	
	public String exec(String cmd) {
		
	   InputStream in = null;
	   String result = null;
	   log.debug(cmd);
	   
	   try {
		   session = conn.openSession(); 
		   session.execCommand(cmd);
		   in = session.getStdout();
	     
		   result = this.processStdout(in, this.charset,session);
		   
		   session.close();
		   
	   } catch (IOException e) {
		   log.error(e.getMessage());
		   session.close();
	   }
	   return result;
	}
	
	public boolean exec2(String cmd) {
		
	   InputStream in = null;
	   log.debug(cmd);
	   
	   try {
		   session = conn.openSession(); 
		   session.execCommand(cmd);
		   in = session.getStdout();
	     
		   this.processStdout(in, this.charset,session);
		   
		   session.close();
		   
	   } catch (IOException e) {
		   log.error(e.getMessage());
		   session.close();
		   return false;
	   }
	   return true;
	}
	
	public String processStdout(InputStream in, String charset ,Session session) {
	   byte[] buf = new byte[1024];
	   StringBuffer sb = new StringBuffer();
	   try {
	    while (in.read(buf) != -1 ) {
	     sb.append(new String(buf, charset));
	    }
	   } catch (IOException e) {
		   log.error(e.getMessage());
	   }
	   return sb.toString();
	}
	
	public boolean exec(String cmd , TaskMessage task ) {
		
	   InputStream in = null;
	   log.debug(cmd);
	   
	   try {
		   session = conn.openSession(); 
		   session.execCommand(cmd);
		   in = session.getStdout();
	     
		   this.processStdout(in, this.charset,session, task);
		   
		   session.close();
		   
	   } catch (IOException e) {
		   log.error(e.getMessage());
		   if (session != null) session.close();
		   return false;
	   }
	   return true;
	}
	
	public void processStdout(InputStream in, String charset ,Session session , TaskMessage task) {
	   StringBuffer sb = new StringBuffer();
	   TaskLogBean taskLog = new TaskLogBean().setTaskNo(task.getTaskNo());
	   Pattern pattern = Pattern.compile("(?<=Starting Job = |proxy/)(.+?)(?=,|/)");
       Matcher matcher ;
       
       int i = 0;

	   try {
		   
		   InputStreamReader input_stream_reader = new InputStreamReader(in, charset);
	       while (true) {
	    	   
	    	   try {
	    		   
	               int c = input_stream_reader.read();
	               
	               sb.append(String.valueOf((char) c));
	               if (String.valueOf((char) c).equals("\n")){
	                   
	            	   String content = sb.toString();
	            	   
	            	   if (StringUtils.isNotBlank(content) && !content.startsWith("SLF4J")
	            			   && !content.contains("Configuration.deprecation")) {
		            	   i++;            	  
		            	   
		            	   // task log into metadb
		            	   DefaultImpl.insertTaskLog(taskLog.setLogSeq(i).setLogContent(StringEscapeUtils.escapeSql(content)));
		            	   
		            	   matcher = pattern.matcher(content);
		            	   
		            	   // insert job into metadb
		            	   int j = 0;
		            	   String job = null,appID = null;
		            	   while (matcher.find()) {
		            		   j++;
		            		   switch (j) {
		            		   	case 1: job = matcher.group();break;
		            		   	case 2: appID = matcher.group();break;
		            		   }
		            	   }
		            	   
		            	   if (StringUtils.isNotEmpty(job) && StringUtils.isNotEmpty(appID)) {
		            		   log.debug(appID + ":" + job);
		            		   DefaultImpl.insertTaskJob(taskLog.setLogSeq(i).setLogJob(job).setAppID(appID));
		            	   }
		            	   
	            	   }
	            	   log.debug(content);
	                   sb.setLength(0);
	               }
	               
	    	   } catch (IOException e) {
	    		   log.error(e.getMessage());
	    	   }
	    	   
	    	   if (!isRunnable) break;
           }
	       
	   } catch (IOException e) {
		   log.error(e.getMessage());
	   }
	}
	
	
	public boolean close() {
		Boolean flag = false;
		try {
			if (conn != null) conn.close();
			flag = true;
		} catch (Exception e){
			   log.error(e.getMessage());
			return flag;
		}
		return flag;
	}
	
	private SCPClient getClient() throws IOException{
		return conn.createSCPClient();		
	}
	
	public void scpFile(String localFile, String remoteTargetDirectory) throws IOException{
		
		SCPClient client = getClient();
		client.put(localFile, remoteTargetDirectory);
		
	}
	
	public void scpFile(List<String> localFiles, String remoteTargetDirectory) throws IOException{
		
		SCPClient client = getClient();
		String[] fileArray = localFiles.toArray(new String[0]);

		// scp files
		client.put(fileArray, remoteTargetDirectory, "0755");
		
		// change DOS to UNIX
	    this.exec(" sed -i 's/\r//' " + remoteTargetDirectory +"/*" );
		
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @return the isRunnable
	 */
	public boolean isRunnable() {
		return isRunnable;
	}

	/**
	 * @param isRunnable the isRunnable to set
	 */
	public void setRunnable(boolean isRunnable) {
		this.isRunnable = isRunnable;
	}
	
}